<template>
  <main
    class="w-full min-h-screen bg-gradient-to-br from-indigo-500 via-blue-400 to-purple-200 overflow-x-hidden relative">
    <div class="content">
      <div class="loading">Loading</div>
      <div class="trigger"></div>
      <div class="section">
        <h1>Airplanes.</h1>
        <h3>The beginners guide.</h3>
        <p>You've probably forgotten what these are.</p>
        <div class="scroll-cta">Scroll</div>
      </div>
      <div class="section right">
        <h2>They're kinda like buses...</h2>
      </div>

      <div class="ground-container">
        <div class="parallax ground"></div>
        <div class="section right">
          <h2>..except they leave the ground.</h2>
          <p>Saaay what!?.</p>
        </div>

        <div class="section">
          <h2>They fly through the sky.</h2>
          <p>For realsies!</p>
        </div>

        <div class="section right">
          <h2>Defying all known physical laws.</h2>
          <p>It's actual magic!</p>
        </div>
        <div class="parallax clouds"></div>
      </div>

      <div class="blueprint">
        <svg width="100%" height="100%" viewbox="0 0 100 100">
          <line id="line-length" x1="10" y1="80" x2="90" y2="80" stroke-width="0.5"></line>
          <path id="line-wingspan" d="M10 50, L40 35, M60 35 L90 50" stroke-width="0.5"></path>
          <circle id="circle-phalange" cx="60" cy="60" r="15" fill="transparent" stroke-width="0.5"></circle>
        </svg>
        <div class="section dark ">
          <h2>The facts and figures.</h2>
          <p>Lets get into the nitty gritty...</p>
        </div>
        <div class="section dark length">
          <h2>Length.</h2>
          <p>Long.</p>
        </div>
        <div class="section dark wingspan">
          <h2>Wing Span.</h2>
          <p>I dunno, longer than a cat probably.</p>
        </div>
        <div class="section dark phalange">
          <h2>Left Phalange</h2>
          <p>Missing</p>
        </div>
        <div class="section dark">
          <h2>Engines</h2>
          <p>Turbine funtime</p>
        </div>
        <!-- 		<div class="section"></div> -->
      </div>
      <div class="sunset">
        <div class="section"></div>
        <div class="section end">
          <h2>Fin.</h2>
          <ul class="credits">
            <li>Plane model by <a href="https://poly.google.com/view/8ciDd9k8wha" target="_blank">Google</a></li>
            <li>Animated using <a href="https://greensock.com/scrolltrigger/" target="_blank">GSAP ScrollTrigger</a>
            </li>
          </ul>
        </div>
      </div>
    </div>
  </main>
</template>

<script setup lang="ts">
import { onMounted } from 'vue'
import * as THREE from 'three'
import { OBJLoader } from 'three/addons/loaders/OBJLoader.js'

const { $gsap, $ScrollTrigger, $DrawSVGPlugin } = useNuxtApp()

class Scene {
  views: Array<{ bottom: number; height: number; camera?: THREE.PerspectiveCamera }>;
  renderer: THREE.WebGLRenderer;
  scene: THREE.Scene;
  light: THREE.PointLight;
  softLight: THREE.AmbientLight;
  modelGroup: THREE.Group;
  w: number = window.innerWidth;
  h: number = window.innerHeight;


  constructor(model: THREE.Group) {
    this.views = [
      { bottom: 0, height: 1 },
      { bottom: 0, height: 0 }
    ];

    this.renderer = new THREE.WebGLRenderer({
      antialias: true,
      alpha: true
    });

    this.renderer.setSize(window.innerWidth, window.innerHeight);
    this.renderer.shadowMap.enabled = true;
    this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
    this.renderer.setPixelRatio(window.devicePixelRatio);

    document.body.appendChild(this.renderer.domElement);

    this.scene = new THREE.Scene();

    for (let ii = 0; ii < this.views.length; ++ii) {
      const view = this.views[ii];
      const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
      camera.position.fromArray([0, 0, 180]);
      camera.layers.disableAll();
      camera.layers.enable(ii);
      view.camera = camera;
      camera.lookAt(new THREE.Vector3(0, 5, 0));
    }

    this.light = new THREE.PointLight(0xffffff, 0.75);
    this.light.position.z = 150;
    this.light.position.x = 70;
    this.light.position.y = -20;
    this.scene.add(this.light);

    this.softLight = new THREE.AmbientLight(0xffffff, 1.5);
    this.scene.add(this.softLight);

    this.onResize();
    window.addEventListener('resize', this.onResize, false);

    const mesh = model.children[0] as THREE.Mesh;
    const edges = new THREE.EdgesGeometry(mesh.geometry);
    const line = new THREE.LineSegments(
      edges,
      new THREE.LineBasicMaterial({ color: 0xffffff, transparent: true, opacity: 0.5 })
    );
    line.position.set(0.5, 0.2, -1);

    this.modelGroup = new THREE.Group();
    model.layers.set(0);
    line.layers.set(1);

    this.modelGroup.add(model);
    this.modelGroup.add(line);
    this.scene.add(this.modelGroup);
  }

  render = (): void => {
    for (let ii = 0; ii < this.views.length; ++ii) {
      const view = this.views[ii];
      const camera = view.camera;
      if (!camera) continue;

      this.renderer.setViewport(0, 0, this.w, this.h);
      this.renderer.setScissor(0, Math.floor(this.h * view.bottom), this.w, Math.floor(this.h * view.height));
      this.renderer.setScissorTest(true);
      camera.aspect = this.w / this.h;
      this.renderer.render(this.scene, camera);
    }
  }

  onResize = (): void => {
    this.w = window.innerWidth;
    this.h = window.innerHeight;

    for (let ii = 0; ii < this.views.length; ++ii) {
      const view = this.views[ii];
      const camera = view.camera;
      if (!camera) continue;

      camera.aspect = this.w / this.h;
      const camZ = (screen.width - (this.w * 1)) / 3;
      camera.position.z = camZ < 180 ? 180 : camZ;
      camera.updateProjectionMatrix();
    }

    this.renderer.setSize(this.w, this.h);
    this.render();
  }
}

const loadModel = (): void => {
  $gsap.registerPlugin($ScrollTrigger);
  $gsap.registerPlugin($DrawSVGPlugin);
  $gsap.set('#line-length', { drawSVG: 0 });
  $gsap.set('#line-wingspan', { drawSVG: 0 });
  $gsap.set('#circle-phalange', { drawSVG: 0 });

  let object: THREE.Group;

  const onModelLoaded = (): void => {
    object.traverse((child) => {
      if (child instanceof THREE.Mesh) {
        const mat = new THREE.MeshPhongMaterial({
          color: 0x171511,
          specular: 0xD0CBC7,
          shininess: 5,
          flatShading: true
        });
        child.material = mat;
      }
    });

    setupAnimation(object);
  };

  const manager = new THREE.LoadingManager(onModelLoaded);
  manager.onProgress = (item: string, loaded: number, total: number) =>
    console.log(item, loaded, total);

  const loader = new OBJLoader(manager);
  loader.load(
    'https://assets.codepen.io/557388/1405+Plane_1.obj',
    (obj: THREE.Group) => { object = obj; }
  );
};


function setupAnimation(model: THREE.Group<THREE.Object3DEventMap>) {
  let scene = new Scene(model);
  let plane = scene.modelGroup;

  $gsap.fromTo('canvas', { x: "50%", autoAlpha: 0 }, { duration: 1, x: "0%", autoAlpha: 1 });
  $gsap.to('.loading', { autoAlpha: 0 })
  $gsap.to('.scroll-cta', { opacity: 1 })
  $gsap.set('svg', { autoAlpha: 1 })

  let tau = Math.PI * 2;

  $gsap.set(plane.rotation, { y: tau * -.25 });
  $gsap.set(plane.position, { x: 80, y: -32, z: -60 });

  scene.render();

  var sectionDuration = 1;
  $gsap.fromTo(scene.views[1],
    { height: 1, bottom: 0 },
    {
      height: 0, bottom: 1,
      ease: 'none',
      scrollTrigger: {
        trigger: ".blueprint",
        scrub: true,
        start: "bottom bottom",
        end: "bottom top"
      }
    })

  $gsap.fromTo(scene.views[1],
    { height: 0, bottom: 0 },
    {
      height: 1, bottom: 0,
      ease: 'none',
      scrollTrigger: {
        trigger: ".blueprint",
        scrub: true,
        start: "top bottom",
        end: "top top"
      }
    })

  $gsap.to('.ground', {
    y: "30%",
    scrollTrigger: {
      trigger: ".ground-container",
      scrub: true,
      start: "top bottom",
      end: "bottom top"
    }
  })

  $gsap.from('.clouds', {
    y: "25%",
    scrollTrigger: {
      trigger: ".ground-container",
      scrub: true,
      start: "top bottom",
      end: "bottom top"
    }
  })

  $gsap.to('#line-length', {
    drawSVG: 100,
    scrollTrigger: {
      trigger: ".length",
      scrub: true,
      start: "top bottom",
      end: "top top"
    }
  })

  $gsap.to('#line-wingspan', {
    drawSVG: 100,
    scrollTrigger: {
      trigger: ".wingspan",
      scrub: true,
      start: "top 25%",
      end: "bottom 50%"
    }
  })

  $gsap.to('#circle-phalange', {
    drawSVG: 100,
    scrollTrigger: {
      trigger: ".phalange",
      scrub: true,
      start: "top 50%",
      end: "bottom 100%"
    }
  })

  $gsap.to('#line-length', {
    opacity: 0,
    drawSVG: 0,
    scrollTrigger: {
      trigger: ".length",
      scrub: true,
      start: "top top",
      end: "bottom top"
    }
  })

  $gsap.to('#line-wingspan', {
    opacity: 0,
    drawSVG: 0,
    scrollTrigger: {
      trigger: ".wingspan",
      scrub: true,
      start: "top top",
      end: "bottom top"
    }
  })

  $gsap.to('#circle-phalange', {
    opacity: 0,
    drawSVG: 0,
    scrollTrigger: {
      trigger: ".phalange",
      scrub: true,
      start: "top top",
      end: "bottom top"
    }
  })

  const tl = $gsap.timeline({
    onUpdate: scene.render,
    scrollTrigger: {
      trigger: ".content",
      scrub: true,
      start: "top top",
      end: "bottom bottom"
    },
    defaults: { duration: sectionDuration, ease: 'power2.inOut' }
  });

  let delay = 0;

  tl.to('.scroll-cta', { duration: 0.25, opacity: 0 }, delay)
  tl.to(plane.position, { x: -10, ease: 'power1.in' }, delay)

  delay += sectionDuration;

  tl.to(plane.rotation, { x: tau * .25, y: 0, z: -tau * 0.05, ease: 'power1.inOut' }, delay)
  tl.to(plane.position, { x: -40, y: 0, z: -60, ease: 'power1.inOut' }, delay)

  delay += sectionDuration;

  tl.to(plane.rotation, { x: tau * .25, y: 0, z: tau * 0.05, ease: 'power3.inOut' }, delay)
  tl.to(plane.position, { x: 40, y: 0, z: -60, ease: 'power2.inOut' }, delay)

  delay += sectionDuration;

  tl.to(plane.rotation, { x: tau * .2, y: 0, z: -tau * 0.1, ease: 'power3.inOut' }, delay)
  tl.to(plane.position, { x: -40, y: 0, z: -30, ease: 'power2.inOut' }, delay)

  delay += sectionDuration;

  tl.to(plane.rotation, { x: 0, z: 0, y: tau * .25 }, delay)
  tl.to(plane.position, { x: 0, y: -10, z: 50 }, delay)

  delay += sectionDuration;
  delay += sectionDuration;

  tl.to(plane.rotation, { x: tau * 0.25, y: tau * .5, z: 0, ease: 'power4.inOut' }, delay)
  tl.to(plane.position, { z: 30, ease: 'power4.inOut' }, delay)

  delay += sectionDuration;

  tl.to(plane.rotation, { x: tau * 0.25, y: tau * .5, z: 0, ease: 'power4.inOut' }, delay)
  tl.to(plane.position, { z: 60, x: 30, ease: 'power4.inOut' }, delay)

  delay += sectionDuration;

  tl.to(plane.rotation, { x: tau * 0.35, y: tau * .75, z: tau * 0.6, ease: 'power4.inOut' }, delay)
  tl.to(plane.position, { z: 100, x: 20, y: 0, ease: 'power4.inOut' }, delay)

  delay += sectionDuration;

  tl.to(plane.rotation, { x: tau * 0.15, y: tau * .85, z: -tau * 0, ease: 'power1.in' }, delay)
  tl.to(plane.position, { z: -150, x: 0, y: 0, ease: 'power1.inOut' }, delay)

  delay += sectionDuration;

  tl.to(plane.rotation, { duration: sectionDuration, x: -tau * 0.05, y: tau, z: -tau * 0.1, ease: 'none' }, delay)
  tl.to(plane.position, { duration: sectionDuration, x: 0, y: 30, z: 320, ease: 'power1.in' }, delay)

  tl.to(scene.light.position, { duration: sectionDuration, x: 0, y: 0, z: 0 }, delay)
}

onMounted(() => {
  loadModel();
});
</script>

<style lang="scss" scoped>
@import url('https://fonts.googleapis.com/css2?family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&display=swap');

svg {
  z-index: 100;
}

:root {
  --padding: 10vmin;
  --color-background: #D0CBC7;

  --font-size-large: 8vw;
  --font-size-medium: 4vw;
  --font-size-normal: 2vw;

  @media only screen and (min-width: 800px) {
    --font-size-large: 64px;
    --font-size-medium: 32px;
    --font-size-normal: 16px;
  }

  @media only screen and (max-width: 500px) {
    --font-size-large: 40px;
    --font-size-medium: 20px;
    --font-size-normal: 14px;
  }
}

a {
  color: white;
}

ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

li {
  margin-top: 10px;
}

html,
body {
  margin: 0;
  // overflow: hidden;
  min-height: 100%;
  min-width: 100%;
  font-family: 'Libre Baskerville', serif;
  background-color: var(--color-background);
  font-weight: 400;
  font-size: var(--font-size-normal);
  overflow-x: hidden;
}

canvas {
  position: fixed;
  z-index: 10;
  top: 0;
  left: 0;
  z-index: 2;
  pointer-events: none;

  visibility: hidden;
  opacity: 0;
}


.solid {
  clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
}

.wireframe {
  clip-path: polygon(0% 100%, 100% 100%, 100% 100%, 0% 100%);
}

.content {
  position: relative;
  z-index: 1;


  .trigger {
    position: absolute;
    top: 0;
    height: 100%;
  }

  .section {
    position: relative;
    padding: var(--padding);
    --pad2: calc(var(--padding) * 2);
    width: calc(100vw - var(--pad2));
    height: calc(100vh - var(--pad2));
    // min-height: 400px;
    margin: 0 auto;
    z-index: 2;

    &.dark {
      color: white;
      background-color: black;
    }

    &.right {
      text-align: right;
    }


  }

  .blueprint {
    position: relative;
    background-color: #131C2A;
    background-image: linear-gradient(rgba(255, 255, 255, 0.1) 1px, transparent 1px),
      linear-gradient(90deg, rgba(255, 255, 255, 0.1) 1px, transparent 1px),
      linear-gradient(rgba(255, 255, 255, 0.05) 1px, transparent 1px),
      linear-gradient(90deg, rgba(255, 255, 255, .05) 1px, transparent 1px);
    background-size: 100px 100px, 100px 100px, 20px 20px, 20px 20px;
    background-position: -2px -2px, -2px -2px, -1px -1px, -1px -1px;
    background-attachment: fixed;

    svg {
      position: fixed;
      top: 0;
      left: 0;
      width: 100vw;
      height: 100vh;
      stroke: white;
      pointer-events: none;
      visibility: hidden;
    }

    .dark {
      background-color: transparent;
    }
  }

  .ground-container {
    position: relative;
    overflow: hidden;
    // perspective: 2px;

    .parallax {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: -100px;
      background-repeat: no-repeat;
      background-position: top center;
      background-size: cover;
      transform-origin: top center;
    }

    .ground {
      z-index: -1;
      background-image: url("https://assets.codepen.io/557388/background-reduced.jpg");
    }

    .clouds {
      z-index: 2;
      background-image: url("https://assets.codepen.io/557388/clouds.png");
    }
  }


  .scroll-cta,
  .credits {
    position: absolute;
    bottom: var(--padding);
  }

  .scroll-cta {
    font-size: var(--font-size-medium);
    opacity: 0;
  }

  .sunset {
    background: url("https://assets.codepen.io/557388/sunset-reduced.jpg") no-repeat top center;
    background-size: cover;
    transform-origin: top center;
  }

  h1,
  h2 {
    font-size: var(--font-size-large);
    margin: 0vmin 0 2vmin 0;
    font-weight: 700;
    display: inline;
  }

  h3 {
    font-size: var(--font-size-medium);
    font-weight: 400;
    margin: 0;
  }

  .end h2 {
    margin-bottom: 50vh;
  }

  .loading {
    position: fixed;
    width: 100vw;
    height: 100vh;
    top: 0;
    left: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--font-size-medium);
  }
}
</style>